<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
    <meta charset="utf-8">
    <title>数据库</title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport"
          content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
    <link rel="stylesheet" href="./layui/css/layui.css" th:href="@{/layui/css/layui.css}" media="all">
    <link rel="stylesheet" href="./style/admin.css" th:href="@{/style/admin.css}" media="all">
    <link rel="stylesheet" href="./style/myself.css" th:href="@{/style/myself.css}" media="all">
    <!-- 让IE8/9支持媒体查询，从而兼容栅格 -->
    <!--[if lt IE 9]>
    <script src="./js/html5.min.js" th:src="@{/js/html5.min.js}"></script>
    <script src="./js/respond.min.js" th:src="@{/js/respond.min.js}"></script>
    <![endif]-->
    <script type="text/javascript" charset="utf-8" th:inline="javascript">
        let ctxPath = /*[[@{/}]]*/'';
        let authority = [[${#authorization.expression('hasAuthority("超级管理员")')}]];
    </script>
    <style type="text/css">
        *{padding: 0;margin: 0;}
        .menu{
            /*这个样式不写，右键弹框会一直显示在画布的左下角*/
            position: absolute;
            background: #ffffff;
            border-radius: 5px;
            left: -99999px;
            top: -999999px;
            z-index: 106;
        }
        .menu ul{list-style: none}
        .menu ul li{
            padding: 2.5px 10px;
            color: #659bc5;
            border-bottom: 1px solid #ffffff;
            font-size: 14px;
            cursor: pointer;
            list-style: none;
        }
        .menu ul li:hover{
            color: #659bc5;
        }
        .menu ul li:last-child{
            border-bottom: none;
            padding: 5px 10px 0 10px;
        }

        .sidebar {
            border-left: #aaa7a7 solid 1px;
            right: 0;
            left: auto;
            position: fixed;
            width: 180px !important;
            height: 100vh;
            padding-right: 15px;
            background-color: #f2f2f2;
            transition: transform 0.3s;
            z-index: 98;
        }

        .sidebar .content {
            padding: 8px;
            z-index: 101;
        }

        .main {
            z-index: 99;
            margin-top: -90px;
            margin-left: -110px;
            transition: transform 0.3s;
        }

        .layui-form-select dl dd{
            color: black;
        }
        .layui-form-select .layui-input {
            width: 150px;
            height: 25px;
            margin-top: 4%;
        }

        .layui-form-item .layui-input-block {
            margin-left: 0;
        }
        .layui-form-checkbox span{
            width: 120px;
        }

        .layui-content {
            overflow: scroll;
            padding-bottom: 20px;
        }

        .div-container {
            display: flex;
            flex-wrap: wrap;
            justify-content: space-between; /* 设置项目之间的间距 */
            width: 85vw;
        }

        .div-container > div {
            box-shadow: 2px 2px 5px #ffffff;
            position: relative;
            flex: 1 1 calc(49% - 20px); /* 让每行最多显示3个项目，每个项目之间有20px的间距 */
            width: 435px;
            height: 410px;
            margin: 10px; /* 设置项目的外边距，创建间距 */
        }

        canvas {
            background-color: #ffffff; /* 设置背景颜色 */
        }

        .toggleBtn{
            text-align: center;
            padding-top: 5px;
            top: 5px;
            width: 30px;
            color: #ffffff;
            height: 25px;
            border: 1px solid;
            background: #1e9fff;
        }
    </style>
</head>
<body>
<div class="layui-layout layui-layout-admin">
    <div class="layui-side sidebar show" style="overflow: visible;">
        <li class="layui-nav-item toggleBtn" style="position: absolute; margin-top: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; ">
            <i class="layui-icon layui-icon-next layui-font-15" style="font-size: 18px"></i>
        </li>
        <div class="content">
            <div id="selectData" style="height:calc(100vh); width: 14%;">
                <form class="layui-form" id="showNameList" style="margin: 5px; margin-top: 40px">
                    <!-- 显示数据为getShowName() -->
                </form>

                <div style="position: absolute; bottom: 30px">
                    <div style="margin-top: 30px; padding-left: 11px">
                        <form class="layui-form" style="margin-top: 15px">
                            <div class="layui-form-item">
                                <div class="layui-input-inline">
                                    <select lay-filter="timeSelect" id="timeSelect">
                                        <option value="30000">30秒</option>
                                        <option value="5000">5秒</option>
                                        <option value="10000">10秒</option>
                                        <option value="60000">1分钟</option>
                                        <option value="300000">5分钟</option>
                                        <option value="600000">10分钟</option>
                                        <option value="pause">暂停</option>
                                    </select>
                                </div>
                            </div>
                        </form>
                    </div>

                    <div style="display: flex; padding-left: 15px">
                        <button type="button" class="layui-btn-sm layui-btn" id="startAll">
                            <i class="layui-icon layui-icon-play layui-font-15"></i>
                            启动
                        </button>
                        <button type="button" class="layui-btn-sm layui-btn layui-btn-danger" id="stopAll">
                            <i class="layui-icon layui-icon-radio layui-font-15"></i>
                            停止
                        </button>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="layui-body main">
        <div class="layui-content">
            <li class="layui-nav-item toggleBtn" style="float: right; margin-top: 10px; border-top-left-radius: 5px; border-bottom-left-radius: 5px;">
                <i class="layui-icon layui-icon-prev layui-font-15" style="font-size: 18px"></i>
            </li>
            <!-- 主体内容 -->
            <div class="div-container" id="container">

            </div>
        </div>
    </div>
</div>

<!--点击节点操作-->
<div id="nodeMenuOperation" class="menu" style="display: none;">
    <ul>
        <li style="margin-top: 10px; margin-bottom: 10px;">
            <button type="button" class="layui-btn-sm layui-btn" id="startNode">
                <i class="layui-icon layui-icon-play layui-font-12"></i>启动
            </button>
            <button type="button" class="layui-btn-sm layui-btn layui-btn-danger" id="stopNode">
                <i class="layui-icon layui-icon-radio layui-font-15"></i>停止
            </button>
        </li>
    </ul>
</div>


<script type="text/javascript" th:src="@{/js/vis-network.min.js}"></script>
<script src="./layui/layui.js" th:src="@{/layui/layui.js}" charset="utf-8"></script>
<script charset="utf-8" th:inline="javascript">
    layui.config({
        base: ctxPath //静态资源所在路径
    }).extend({
        index: 'lib/index' //主入口模块
    }).use(['index', 'element', 'layer', 'form', 'jquery', 'util'], function () {
        let admin = layui.admin;
        let form = layui.form;
        let $ = layui.$
        let clickNodeList = {id: 1}, clickEdgesList = {id: 1};
        let time = 30000;
        let timer;
        let visioInstances = new Map();//存储所有链路实例 key是showName value是实例

        let sidebar = $('.sidebar');
        let main = $('.main');
        sidebar.addClass('show');
        sidebar.css('transform', 'translateX(0)'); //左边侧拉显示
        main.css('transform', 'translateX(-110px)'); //主体内容移动到右边

        // 创建节点数据数组
        let index = layer.load(0, {shade: false}); //0代表加载的风格，支持0-2

        // 随机生成虚线间隔长度
        function getRandomDashes() {
            let dashes = [];
            for (let i = 0; i < 5; i++) {
                dashes.push(Math.random() * 5);
            }
            return dashes;
        }
        //线条动画
        setInterval(function() {
            let newDashes = getRandomDashes();
            for (let [key] of visioInstances) {
                let instance = visioInstances.get(key);
                instance.body.data.edges.forEach(function (edge) {
                    instance.body.data.edges.update({ id: edge.id, width: 3, dashes: newDashes}); // 更新边的宽度
                })
            }
        }, 150);

        $(document).ready(function() {
            //显示左侧选项数据
            getShowName()
        });

        //定时器，每几秒请求一下数据然后渲染数链图
        timer = setInterval(function() {
            this.isScaled = false
            loadData()
        },time)

        //监听页面关闭或离开事件
        window.onbeforeunload = function() {
            // 停止定时器
            clearInterval(timer);
        };

        function getShowName(){
            admin.req({
                type: 'get',
                url: ctxPath + 'dataLink/getShowName',
                dataType: 'json',
                headers: {
                    [[${_csrf.headerName}]]: [[${_csrf.token}]]
                },
                success: function (result) {
                    let resData = result.data;
                    let container = document.getElementById("showNameList");
                    resData.forEach(function(item) {
                        let div1 = document.createElement("div");
                        div1.className = "layui-form-item selectBox";

                        let div2 = document.createElement("div");
                        div2.className = "layui-input-block selectBox";

                        let input = document.createElement("input");
                        input.type = "checkbox";
                        input.setAttribute("lay-filter", "checkEvent");
                        input.checked = true;
                        input.id = "checkBox:" + item.showName
                        input.title = item.showName;

                        div2.appendChild(input);
                        div1.appendChild(div2);
                        container.appendChild(div1);

                        layui.form.render(); // 渲染表单元素

                        //创建一个用来挂载visio的div
                        let container1 = document.getElementById("container");
                        let div = document.createElement("div");
                        div.id = item.showName
                        container1.appendChild(div);
                        //创建链路并添加到map
                        createVisio(item.showName)
                    })
                    //loadData()
                },
                error: function () {
                    layer.close(index);
                    layer.msg('系统错误！', {icon: 5, shift: 6});
                }
            });
        }

        //刷新所有页面的visio实例
        async function loadData(){
            for (let [key] of visioInstances) {
                let instance = visioInstances.get(key);
                admin.req({
                    type: 'get',
                    url: ctxPath + 'dataLink/getDataLink?showName=' + key,
                    dataType: 'json',
                    headers: {
                        [[${_csrf.headerName}]]: [[${_csrf.token}]]
                    },
                    success: function (result) {
                        let resData = result.data;
                        let promises = []; // 用于存储所有的Promise
                        for (let i = 0; i < resData.links.length; i++) {
                            if(resData.links[i].color == 'red'){
                                let promise = new Promise((resolve, reject) => {
                                    admin.req({
                                        type: 'get',
                                        url: ctxPath + 'dataLink/getErrMsg',
                                        data: {from : resData.links[i].from,
                                            to : resData.links[i].to,
                                            label : resData.links[i].label
                                        },
                                        dataType: 'json',
                                        headers: {
                                            [[${_csrf.headerName}]]: [[${_csrf.token}]]
                                        },
                                        success: function (result) {
                                            if (Array.isArray(result.data) && result.data.length > 0 && !(result.data.length === 1 && result.data[0] === null)) {
                                                let info = "";
                                                for (let j = 0; j < result.data.length; j++) {
                                                    info = info + result.data[j].message;
                                                }
                                                resData.links[i].label = info
                                            }
                                            resolve(); // 请求成功后resolve这个Promise
                                        },
                                        error: function (error) {
                                            reject(error); // 请求失败后reject这个Promise
                                        }
                                    })
                                });

                                promises.push(promise); // 将这个Promise添加到数组中
                            }
                        }
                        Promise.all(promises).then(() => {
                            // 这里的代码会在所有的admin.req请求完成后执行
                            instance.setData({
                                nodes: resData.nodes,
                                edges: resData.links
                            });
                            layer.close(index);
                        });
                    },
                    error: function () {
                        layer.close(index);
                        layer.msg('系统错误！', {icon: 5, shift: 6});
                    }
                });
            }
        }

        //获取节点 获取线
        function getNode(nodes, option) {
            for (let i = 0; i < nodes.length; i++) {
                if (option == nodes[i].id) {
                    return nodes[i];
                }
            }
        }
        function getEdge(edges, option) {
            for (let i = 0; i < edges.length; i++) {
                if (option == edges[i].id) {
                    return edges[i];
                }
            }
        }

        //按钮
        $('#startNode').off('click').on('click', function(){
            layer.open({
                type: 1,
                title: '启动节点（' + clickNodeList.id + '）',
                area: ['25%', 'auto'],
                content: `
                                        <form class="layui-form" style="margin: 15px;">
                                            <div class="layui-form-item">
                                                <div class="layui-input-block">
                                                    <input type="text" name="username" required lay-verify="required" placeholder="请输入用户名" autocomplete="off" class="layui-input">
                                                </div>
                                            </div>
                                            <div class="layui-form-item">
                                                <div class="layui-input-block">
                                                    <input type="password" name="password" required lay-verify="required" placeholder="请输入密码" autocomplete="off" class="layui-input">
                                                </div>
                                            </div>
                                            <div class="layui-form-item">
                                                <input type="hidden" name="type" value="0">
                                            </div>
                                            <div class="layui-form-item">
                                                <div class="layui-input-block">
                                                    <button class="layui-btn" lay-submit lay-filter="operateNode">执行</button>
                                                </div>
                                            </div>
                                        </form>
                                    `
            });
        });
        $('#stopNode').off('click').on('click', function(){
            layer.open({
                type: 1,
                title: '停止节点（' + clickNodeList.id + '）',
                area: ['25%', 'auto'],
                content: `
                                            <form class="layui-form" style="margin: 15px;">
                                                <div class="layui-form-item">
                                                    <div class="layui-input-block">
                                                        <input type="text" name="username" required lay-verify="required" placeholder="请输入用户名" autocomplete="off" class="layui-input">
                                                    </div>
                                                </div>
                                                <div class="layui-form-item">
                                                    <div class="layui-input-block">
                                                        <input type="password" name="password" required lay-verify="required" placeholder="请输入密码" autocomplete="off" class="layui-input">
                                                    </div>
                                                </div>
                                                <div class="layui-form-item">
                                                    <input type="hidden" name="type" value="1">
                                                </div>
                                                <div class="layui-form-item">
                                                    <div class="layui-input-block">
                                                        <button class="layui-btn" lay-submit lay-filter="operateNode">执行</button>
                                                    </div>
                                                </div>
                                            </form>
                                        `
            });
        });
        $('#startAll').off('click').on('click', function(){
            layer.confirm('确定执行该操作吗？', {
                title: '启动所有线程',
            },function(index){
                // 点击确定按钮的操作

                // 关闭确认框
                layer.close(index);
            });
        })
        $('#stopAll').off('click').on('click', function(){
            layer.confirm('确定执行该操作吗？', {
                title: '停止所有线程',
            },function(index){
                // 点击确定按钮的操作

                // 关闭确认框
                layer.close(index);
            });
        })

        //启动和停止节点线程
        form.on('submit(operateNode)', function(data){
            let $this = $(this);
            $this.html('<i class="layui-icon layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i> 执行中...');
            $this.prop('disabled', true); // 禁用按钮
            data.field.ip = clickNodeList.id
            let info = data.field

            admin.req({
                type: 'post',
                url: ctxPath + 'dataLink/operateNode',
                data: {info: JSON.stringify(info)},
                dataType: 'json',
                headers: {
                    [[${_csrf.headerName}]]: [[${_csrf.token}]]
                },
                success: function (result) {
                    layer.close(1);
                    let msg = "";
                    if(result.code == 200){
                        msg = "执行成功";
                    }else{
                        msg = "执行失败";
                    }
                    layer.msg(msg);
                    $this.html('执行');
                    $this.prop('disabled', false); // 启用按钮
                }
            })
            // 阻止表单跳转
            return false;
        });

        // 点击按钮切换侧边栏显示/隐藏
        $('.toggleBtn').on('click', function() {
            let sidebar = $('.sidebar');
            let main = $('.main');
            let icon = document.querySelector(".toggleBtn i");
            if (sidebar.hasClass('show')) {
                // 更改 class 属性
                icon.classList.remove("layui-icon-next");
                icon.classList.add("layui-icon-prev");

                sidebar.removeClass('show');
                sidebar.css('transform', 'translateX(240px)'); //左边侧拉隐藏
                main.css('transform', 'translateX(0)'); //主体内容移动到原来侧边栏的位置
            } else {
                // 更改 class 属性
                icon.classList.remove("layui-icon-prev");
                icon.classList.add("layui-icon-next");

                sidebar.addClass('show');
                sidebar.css('transform', 'translateX(0)'); //左边侧拉显示
                main.css('transform', 'translateX(-110px)'); //主体内容移动到右边
            }
        });

        // 监听下拉菜单选择事件
        form.on('select(timeSelect)', function(data) {
            if (data.value === 'pause') {
                // 处理暂停逻辑
                clearInterval(timer);
            } else {
                clearInterval(timer);
                // 处理其他时间逻辑
                timer = setInterval(function() {
                    loadData()
                }, data.value)
            }
        });

        // 监听指定开关
        form.on('checkbox(checkEvent)', function(data){
            let title = data.elem.getAttribute('title')
            if(data.elem.checked){
                //创建一个用来挂载visio的div
                let container = document.getElementById("container");
                let div = document.createElement("div");
                div.id = title
                container.appendChild(div);
                //创建链路并添加到map
                createVisio(title)
            } else {
                //删除map的key并删除页面的visio实例挂载的div元素
                visioInstances.delete(title)
                let container = document.getElementById("container");
                let divToRemove = document.getElementById(title);
                container.removeChild(divToRemove);
                loadData()
            }
        });

        /* 根据左侧创建visio并将实例存到visioInstances里 */
        function createVisio(id) {
            // 获取容器
            let container = document.getElementById(id);
            container.style.cursor = 'pointer';

            // 将数据赋值给vis 数据格式化器
            let visData = {
                nodes: [],
                edges: []
            };
            let options = {
                physics: {
                    enabled: true,
                    barnesHut: {
                        gravitationalConstant: -2000,
                        centralGravity: 0.3,
                        springLength: 950,
                        springConstant: 0.04,
                        damping: 0.09,
                        avoidOverlap: 0.1
                    },
                    maxVelocity: 146,
                    minVelocity: 0.1,
                    solver: 'barnesHut',
                    timestep: 0.35,
                    stabilization: {
                        enabled: true,
                        iterations: 2000,
                        updateInterval: 25
                    }
                },
                //固定布局，保证每次渲染的位置都一样
                layout: {
                    randomSeed: 1024
                },
                //设置节点
                nodes: {
                    size: 18,
                    font: {
                        color: 'black',
                        align: 'left',
                        size: 11,
                        vadjust: -10, // 将文字向上移动15个像素
                        strokeWidth: 0.2, // 描边宽度
                        strokeColor : 'black' // 描边颜色
                    }
                },
                edges: {
                    dashes: true,//虚线
                    width: 2,  // 设置默认的边宽度为3
                    smooth: {
                        //设置两个节点之前的连线的状态
                        enabled: true //默认是true，设置为false之后，两个节点之前的连线始终为直线，不会出现贝塞尔曲线
                    },
                    //箭头
                    arrows: {
                        to: {
                            enabled: true,
                            scaleFactor: 0.5,
                            type: "arrow"
                        },
                        middle: {
                            enabled: true,
                            scaleFactor: 0.5,
                            type: "arrow"
                        }
                    }
                }
            };
            // 初始化关系图
            let network = new vis.Network(container, visData, options);
            network.fit();
            visioInstances.set(id, network);

            // 获取测试链路的父元素
            let parentDiv = document.getElementById(id);

            // 创建头部元素 标题
            let title = document.createElement('div');
            title.textContent = id;
            title.style.position = 'absolute';
            title.style.top = '5px';
            title.style.left = '8px';
            title.style.zIndex = '102';

            // 创建头部元素 关闭
            let close = document.createElement('i');
            close.classList.add('layui-icon');
            close.classList.add('layui-icon-close');
            close.style.position = 'absolute';
            close.style.right = '8px';
            close.style.zIndex = '102';
            close.style.top = '1%';
            close.style.float = "right"
            close.style.fontSize = "20px"
            close.onclick = function (){
                visioInstances.delete(id)
                let container = document.getElementById("container");
                let divToRemove = document.getElementById(id);
                container.removeChild(divToRemove);

                let input = document.getElementById("checkBox:" + id);
                input.checked = false;
                layui.form.render(); // 渲染表单元素

                loadData()
            }

            // 创建头部元素 全屏
            let detail = document.createElement('i');
            detail.classList.add('layui-icon');
            detail.classList.add('layui-icon-screen-full');
            detail.style.position = 'absolute';
            detail.style.top = '1.5%';
            detail.style.right = '35px';
            detail.style.zIndex = '102';
            detail.style.float = "right"
            detail.style.fontSize = "18px"
            detail.onclick = function (){
                console.log("全屏：" + id)
                localStorage.setItem("showName", id)
                layer.open({
                    type: 2,
                    title: '数链详情（' + id + '）',
                    closeBtn: 1,
                    shadeClose: true,
                    area: ['100%', '100%'],
                    content: ctxPath + 'dataLink/visioDetail',
                });
            }


            // 将头部元素添加到测试链路的父元素中
            parentDiv.insertBefore(title, parentDiv.firstChild);
            parentDiv.insertBefore(close, parentDiv.firstChild);
            parentDiv.insertBefore(detail, parentDiv.firstChild);

            admin.req({
                type: 'get',
                url: ctxPath + 'dataLink/getDataLink?showName=' + id,
                dataType: 'json',
                headers: {
                    [[${_csrf.headerName}]]: [[${_csrf.token}]]
                },
                success: function (result) {
                    let resData = result.data;
                    let promises = []; // 用于存储所有的Promise
                    for (let i = 0; i < resData.links.length; i++) {
                        if(resData.links[i].color == 'red'){
                            let promise = new Promise((resolve, reject) => {
                                admin.req({
                                    type: 'get',
                                    url: ctxPath + 'dataLink/getErrMsg',
                                    data: {from : resData.links[i].from,
                                        to : resData.links[i].to,
                                        label : resData.links[i].label
                                    },
                                    dataType: 'json',
                                    headers: {
                                        [[${_csrf.headerName}]]: [[${_csrf.token}]]
                                    },
                                    success: function (result) {
                                        if (Array.isArray(result.data) && result.data.length > 0 && !(result.data.length === 1 && result.data[0] === null)) {
                                            let info = "";
                                            for (let j = 0; j < result.data.length; j++) {
                                                info = info + result.data[j].message;
                                            }
                                            resData.links[i].label = info
                                        }
                                        resolve(); // 请求成功后resolve这个Promise
                                    },
                                    error: function (error) {
                                        reject(error); // 请求失败后reject这个Promise
                                    }
                                })
                            });

                            promises.push(promise); // 将这个Promise添加到数组中
                        }
                    }
                    Promise.all(promises).then(() => {
                        // 这里的代码会在所有的admin.req请求完成后执行
                        network.setData({
                            nodes: resData.nodes,
                            edges: resData.links
                        });

                        //点击的判断是否选中节点时候显示隐藏
                        network.on('click', function (properties) {
                            if (properties.nodes.length > 0) {
                                clickNodeList = getNode(resData.nodes, properties.nodes[0]);
                                if (typeof (clickNodeList) == "undefined") {
                                    $('#nodeMenuOperation').hide();
                                } else {
                                    $('#nodeMenuOperation').css({
                                        'display': 'block',
                                        'left': properties.event.center.x + 15,
                                        'top': properties.event.center.y + 15
                                    });
                                }
                            } else if (properties.edges.length > 0) {
                                clickEdgesList = getEdge(resData.links, properties.edges[0]);
                            } else {
                                //点击画布，不是节点，不是线，隐藏div
                                $("#nodeMenuOperation").hide();
                                $("#edgeMenuOperation").hide();
                            }
                        });

                        loadData()
                    });
                },
                error: function () {
                    layer.close(index);
                    layer.msg('系统错误！', {icon: 5, shift: 6});
                }
            });
        }

    })
</script>
</body>
</html>
